package cn.iocoder.yudao.module.picturebook.utils;

import com.alibaba.fastjson.JSON;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.After;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Component;

import javax.annotation.Resource;
import java.lang.reflect.Method;
import java.util.Collection;
import java.util.Set;
import java.util.concurrent.TimeUnit;

@Aspect
@Component
@Slf4j
public class MysqlRedisCacheAspect {

    @Resource
    private RedisTemplate<String, Object> redisTemplate;

    // 定义命名空间前缀
    private static final String CACHE_NAMESPACE = "picturebook:";

    // 查询方法拦截
    @Around("execution(* cn.iocoder.yudao.module.picturebook.dal.*Mapper.*select*(..)) || " +
            "execution(* cn.iocoder.yudao.module.picturebook.dal.*Mapper.*get*(..))")
    public Object cacheQuery(ProceedingJoinPoint joinPoint) throws Throwable {
        String key = generateCacheKey(joinPoint);

        try {
            Object cacheValue = redisTemplate.opsForValue().get(key);
            if (cacheValue != null) {
                log.debug("Redis缓存命中: {}", key);
                return cacheValue;
            }
        } catch (Exception e) {
            log.warn("Redis查询失败: {}", key, e);
        }

        Object result = joinPoint.proceed();

        if (result != null) {
            try {
                // 根据查询类型设置不同的过期时间
                long expireTime = getExpireTime(joinPoint);
                redisTemplate.opsForValue().set(key, result, expireTime, TimeUnit.SECONDS);
                log.debug("数据存入Redis缓存: {}，过期时间: {}秒", key, expireTime);
            } catch (Exception e) {
                log.warn("Redis写入失败: {}", key, e);
            }
        }

        return result;
    }

    // 写操作拦截 - 使用 @After 确保事务提交后执行
    @After("execution(* cn.iocoder.yudao.module.picturebook.dal.mysql.*Mapper.*insert*(..)) || " +
            "execution(* cn.iocoder.yudao.module.picturebook.dal.mysql.*Mapper.*update*(..)) || " +
            "execution(* cn.iocoder.yudao.module.picturebook.dal.mysql.*Mapper.*delete*(..)) || " +
            "execution(* cn.iocoder.yudao.module.picturebook.dal.mysql.*Mapper.*create*(..))")
    public void clearCacheAfterWrite(JoinPoint joinPoint) {
        String className = joinPoint.getTarget().getClass().getName();
        String methodName = joinPoint.getSignature().getName();

        log.debug("检测到数据库写操作: {}.{}", className, methodName);

        // 获取实体ID
        Object entityId = getEntityId(joinPoint);

        // 精确删除特定实体的缓存，而不是扫描删除大量key
        if (!"NO_ID".equals(entityId) && !"UNKNOWN".equals(entityId)) {
            String entityKey = CACHE_NAMESPACE + className + ":" + entityId;
            try {
                redisTemplate.delete(entityKey);
                log.debug("清理实体缓存: {}", entityKey);
            } catch (Exception e) {
                log.warn("清理实体缓存失败: {}", entityKey, e);
            }
        }

        // 删除该Mapper相关的列表缓存（使用keys方式，但仍比scan更高效）
        String listPattern = CACHE_NAMESPACE + className + ":list:*";
        try {
            Set<String> keys = redisTemplate.keys(listPattern);
            if (keys != null && !keys.isEmpty()) {
                redisTemplate.delete(keys);
                log.debug("清理列表缓存: {}，共{}个key", listPattern, keys.size());
            }
        } catch (Exception e) {
            log.warn("清理列表缓存失败: {}", listPattern, e);
        }
    }

    // 生成带命名空间的缓存Key
    private String generateCacheKey(ProceedingJoinPoint joinPoint) {
        String className = joinPoint.getTarget().getClass().getName();
        String methodName = joinPoint.getSignature().getName();

        StringBuilder sb = new StringBuilder(CACHE_NAMESPACE);
        sb.append(className).append(":");

        // 判断是否为列表查询
        if (methodName.startsWith("select") || methodName.startsWith("get")) {
            if (isListMethod(joinPoint)) {
                sb.append("list:");
            } else {
                // 获取实体ID作为缓存Key的一部分
                Object entityId = getEntityId(joinPoint);
                sb.append(entityId).append(":");
            }
        }

        sb.append(methodName).append(":");

        // 添加参数信息
        Object[] args = joinPoint.getArgs();
        if (args != null && args.length > 0) {
            sb.append(JSON.toJSONString(args));
        } else {
            sb.append("NO_ARGS");
        }

        return sb.toString();
    }

    // 判断是否为列表查询方法
    private boolean isListMethod(ProceedingJoinPoint joinPoint) {
        Class<?> returnType = ((org.aspectj.lang.reflect.MethodSignature) joinPoint.getSignature()).getReturnType();
        return Collection.class.isAssignableFrom(returnType) || returnType.isArray();
    }

    // 提取实体ID
    private Object getEntityId(JoinPoint joinPoint) {
        Object[] args = joinPoint.getArgs();
        if (args == null || args.length == 0) {
            return "NO_ID";
        }

        Object firstArg = args[0];
        if (firstArg instanceof Long || firstArg instanceof Integer) {
            return firstArg;
        }

        try {
            Method getIdMethod = firstArg.getClass().getMethod("getId");
            return getIdMethod.invoke(firstArg);
        } catch (Exception e) {
            return "UNKNOWN";
        }
    }

    // 根据查询类型获取合适的过期时间
    private long getExpireTime(ProceedingJoinPoint joinPoint) {
        // 列表查询设置较短的过期时间(10分钟)
        if (isListMethod(joinPoint)) {
            return 600;
        }
        // 单个实体查询设置较长的过期时间(1小时)
        return 3600;
    }
}
